package cn.touna.jss.modules.third.zixin;

import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.jss.common.factory.ResponseFactory;
import cn.touna.jss.common.properties.disconf.ShanghaiZixinProperties;
import cn.touna.jss.common.util.RaderXml;
import cn.touna.jss.common.util.ValidUtil;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.third.zixin.entity.ZiXinEntity;
import cn.touna.risk.api.domain.Response;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.List;

/**
 * Created by Steve.Tao on 2017/10/24.
 */
@Service
public class ZixinService {

    private static final Logger logger = LogManager.getLogger(ZixinService.class);

    @Autowired
    private ShanghaiZixinProperties zixinProperties;

    HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {

        public boolean retryRequest(
                IOException exception,
                int executionCount,
                HttpContext context) {
            if (executionCount >= 5) {
                // Do not retry if over max retry count
                return false;
            }
            if (exception instanceof NoHttpResponseException) {
                // Retry if the server dropped connection on us
                return true;
            }
            if (exception instanceof SSLHandshakeException) {
                // Do not retry on SSL handshake exception
                return false;
            }
            HttpRequest request = (HttpRequest) context.getAttribute(
                    ExecutionContext.HTTP_REQUEST);
            boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
            if (idempotent) {
                // Retry if the request is considered idempotent
                return true;
            }
            return false;
        }
    };

    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSLv3");

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    private CloseableHttpClient buildHttpClient() throws KeyManagementException, NoSuchAlgorithmException {
        ConnectionKeepAliveStrategy kaStrategy = new DefaultConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                long keepAlive = super.getKeepAliveDuration(response, context);
                if (keepAlive == -1) {
                    //如果服务器没有设置keep-alive这个参数，我们就把它设置成1分钟
                    keepAlive = 20;
                }
                return keepAlive;
            }
        };
        SSLContext sslContext = createIgnoreVerifySSL();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslContext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
//        HttpHost httpHost = new HttpHost("vpn.shanghai-cis.com.cn", 80);
        // 设置协议http和https对应的处理socket链接工厂的对象
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .setKeepAliveStrategy(kaStrategy)
                .setRetryHandler(myRetryHandler)
                .build();
        return httpClient;
    }


    @ApiMapping(value="ZiXin.queryCreditInfo",desc="上海资信.资信报告查询")
    public Response queryCreditInfo(ZiXinEntity request) {
        Response response = ValidUtil.valid(request);
        if (response != null)
            return response;

        CloseableHttpClient httpClient = null;
        try {
            httpClient = buildHttpClient();
        } catch (Exception e) {
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (IOException e1) {
                    logger.error(e);
                }
            }
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR, "网络异常，" + e.getMessage());
        }

        try {
            HttpPost login = new HttpPost(zixinProperties.getStep1URI());
            login.addHeader("Content-Type", "application/x-www-form-urlencoded");
            login.setConfig(requestConfig);
            CloseableHttpResponse loginRes = null;
            try {
                loginRes = httpClient.execute(login);
//                if(loginRes.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
//                    login.abort();
//                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR,loginRes.getStatusLine().toString());
//                }
                login.abort();
            } catch (Exception e) {
                logger.error("模拟登录失败1", e);
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, "模拟登录失败1，" + e.getMessage());
            } finally {
                login.releaseConnection();
                if (loginRes != null) {
                    try {
                        loginRes.close();
                    } catch (IOException e) {
                        logger.error(e);
                    }
                }
            }

            HttpPost login2 = new HttpPost(zixinProperties.getStep2URI());
            login2.setConfig(requestConfig);
            login2.addHeader("Content-Type", "application/x-www-form-urlencoded");
            login2.addHeader("Refer", zixinProperties.getStep1URI());
            login2.addHeader("Accept",
                    "image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");

            NameValuePair tgroup = new BasicNameValuePair("tgroup", "");
            NameValuePair next = new BasicNameValuePair("next", "");
            NameValuePair tgcookieset = new BasicNameValuePair("tgcookieset", "");
            NameValuePair Login = new BasicNameValuePair("Login", "登陆");
            NameValuePair Email = new BasicNameValuePair("username", zixinProperties.getUsername());
            NameValuePair password1 = new BasicNameValuePair("password", zixinProperties.getPassword());
            List<NameValuePair> params = Arrays.asList(tgroup, next, tgcookieset, Login, Email, password1);
            login2.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));

            CloseableHttpResponse loginRes2 = null;
            try {
                loginRes2 = httpClient.execute(login2);
//                if(loginRes2.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
//                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR,loginRes2.getStatusLine().toString());
                login2.abort();
            } catch (Exception e) {
                logger.error("模拟登录失败2", e);
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, "模拟登录失败2，" + e.getMessage());
            } finally {
                login2.releaseConnection();
                if (loginRes2 != null) {
                    try {
                        loginRes2.close();
                    } catch (Exception e) {
                        logger.error("loginRes2关闭失败", e);
                    }
                }
            }

            String name = request.getName();// 查询姓名
            String reason = request.getReason();// 查询原因
            String certno = request.getIdCard();// 证件号码
            String certtype = request.getCerttype();// 证件类型

            HttpPost queryCredit = new HttpPost(zixinProperties.getBatchCreditWebServiceURI());
            queryCredit.setConfig(requestConfig);
            queryCredit.addHeader("http.protocol.content-charset", "utf-8");
            queryCredit.addHeader("Content-Type", "application/x-www-form-urlencoded");
            String requeststr =
                    "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://webservice.creditreport.p2p.sino.com/\">" +
                            "<soapenv:Header/>" +
                            "<soapenv:Body>" +
                            "<web:queryCredit>" +
                            "<orgcode>" + zixinProperties.getOrgcode() + "</orgcode>" +
                            "<secret>" + zixinProperties.getSsecret() + "</secret>" +
                            "<plate>" + 1 + "</plate>" +
                            "<certtype>" + certtype + "</certtype>" +
                            "<certno>" + certno + "</certno>" +
                            "<name>" + name + "</name>" +
                            "<reason>" + reason + "</reason>" +
                            "<createtype>" + 1 + "</createtype>" +
                            "</web:queryCredit>" +
                            "</soapenv:Body>" +
                            "</soapenv:Envelope>";

            StringEntity stringEntity = new StringEntity(requeststr, "UTF-8");
            queryCredit.setEntity(stringEntity);
            CloseableHttpResponse queryCreditRes = null;
            try {
                queryCreditRes = httpClient.execute(queryCredit);

                if (queryCreditRes.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                    logger.error("返回结果为空，response status is : " + queryCreditRes.getStatusLine());
                    if (queryCreditRes.getStatusLine().getStatusCode() == 302) {
                        Header[] headers = queryCreditRes.getHeaders("location");
                        if (headers != null && headers.length > 0) {
                            for (Header header : headers) {
                                logger.info("header name:" + header.getName() + " , value: " + header.getValue());
                            }
                        }
                    }
                    queryCredit.abort();
                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, queryCreditRes.getStatusLine().toString());
                }

                String resStr = EntityUtils.toString(queryCreditRes.getEntity());
                if (StringUtils.isEmpty(resStr)) {
                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, "返回结果为空");
                }
                String res = extract(resStr);
                if (StringUtils.isEmpty(res)) {
                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, "返回结果为空");
                }
                String jRes = null;
                try {
                    jRes = RaderXml.xmlToJsonString(res);
                } catch (Exception e) {
                    jRes = res;
                }
                try {
                    //extract方法提取结果时，成功则直接提取有效结果，失败时会返回{state:xx,errors}
                    JSONObject jObj = JSONObject.parseObject(jRes);
                    Integer state = jObj.getInteger("state");
                    if (state == null || state == 1)  //返回正常的情况
                        return ResponseFactory.success(jRes);
                    String errors = jObj.getString("errors");
                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, errors,jRes);
                } catch (Exception e) {
                    logger.error("调用上海资信信用报告失败", e);
                    return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL, "JSON格式不完善，报文为：" + jRes + ",异常为" + e.getMessage(),jRes);
                }
            } catch (Exception e) {
                logger.error("调用上海资信信用报告失败", e);
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
            } finally {
                queryCredit.releaseConnection();
                if (queryCreditRes != null) {
                    try {
                        queryCreditRes.close();
                    } catch (Exception e) {
                        logger.error("queryCreditRes关闭失败", e);
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
        } finally {
            try {
                if (httpClient != null)
                    httpClient.close();
            } catch (IOException e) {
                logger.error("httpClient关闭失败", e);
            }
        }
    }

    private String extract(String result) {
        logger.info("上海资信原始返回报文：" + result);
        String res = null;
        if (null != result && !result.contains("errors&quot")) {
            String newstr = result.substring(result.indexOf("<return>") + 35,
                    result.lastIndexOf("</return>") - 28);// 第35个和28个
            res = StringEscapeUtils.unescapeHtml(newstr.replaceAll("\\\\", ""));
        } else {
            String newstr = StringEscapeUtils.unescapeHtml(result.replaceAll("\\\\", ""));
            res = newstr.substring(newstr.indexOf("<return>") + 9, result.lastIndexOf("</return>") - 31);
        }
        return res;
    }

    private RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(120000)
            .setConnectTimeout(120000)
            .setConnectionRequestTimeout(120000)
//            .setProxy(new HttpHost("vpn.shanghai-cis.com.cn", 80))
            .build();
}